home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 31 / Amiga Format CD31 (1998-09-02)(Future Publishing)(GB)(Track 1 of 2)[!][issue 1998-10].iso / -seriously_amiga- / misc / cpublit98 / src / cpublit.c next >
C/C++ Source or Header  |  1998-07-16  |  22KB  |  538 lines

  1. /****************************************************************************
  2.  *
  3.  *              CPUBLIT.C
  4.  *
  5.  *              (C) Copyright Eddy Carroll, 1991. Freely distributable.
  6.  *
  7.  *              CpuBlit replaces the BltBitMap function in graphics.library with
  8.  *              a version that uses the CPU where practical. This is up to 2.8
  9.  *              times faster on a 68030 system.
  10.  *
  11.  *              This module installs the new blit routine, handles parsing the
  12.  *              command line options etc. Scroll.s does the actual blitting.
  13.  *
  14.  *              03/Jul/98: - small changes for SAS/C 6.58 68040 version (ARK)
  15.  *                         - ANSI-fied
  16.  *                         - fixed some typos
  17.  *
  18.  ***************************************************************************/
  19.  
  20. #define DEBUG   0                       /* If 1, then include debugging code                    */
  21.  
  22. typedef void (*__fptr)();       /* The sort of thing returned by SetFunction    */
  23.  
  24. #include <exec/types.h>
  25. #include <exec/execbase.h>
  26.  
  27. #include <libraries/dos.h>
  28. #include <libraries/dosextens.h>
  29.  
  30. #include <intuition/intuition.h>
  31.  
  32. #include <workbench/startup.h>
  33. #include <workbench/workbench.h>
  34.  
  35. #include <proto/exec.h>
  36. #include <proto/dos.h>
  37. #include <proto/graphics.h>
  38. #include <proto/intuition.h>
  39. #include <proto/icon.h>
  40.  
  41. #include <stdio.h>
  42. #include <stdlib.h>
  43. #include <string.h>
  44.  
  45. #include "scroll.h"
  46. #include "res.h"
  47.  
  48. #define YES             1
  49. #define NO              0
  50.  
  51. #define NAME                    "CpuBlit V1.00"
  52. #define PORTNAME                NAME
  53. #define SIGNON                  NAME " \251 1991 Eddy Carroll. (1998 ARK recompile)\n"
  54. #define DATE                    "03/Jul/98"
  55.  
  56. #define print(s)                Write(Output(), s, strlen(s))
  57. #define BltBitMap_LVO   (-30)   /* Offset of BltBitMap in graphics.library      */
  58.  
  59. char HelpMsg[] =
  60. NAME " \251 Eddy Carroll (1998 ARK recompile), " DATE ".\nReplaces blitter with 68020/68030.\n"
  61. "Usage: CpuBlit {options} | {keywords}\n"
  62. "\n"
  63. "Options:\n"
  64. "    -a    Always use CPU to do blits (default setting)\n"
  65. "    -1    Use CPU for blits unless another task is ready to run\n"
  66. "    -2    Use CPU for blits unless more than one task is ready to run\n"
  67. "    -b    Use CPU even if bitmap isn't initialised correctly\n"
  68. "    -o    Use CPU only when a single bitmap is involved\n"
  69. "    -s    Use CPU for blits unless a blit is already in progress\n"
  70. "    -pN   Ignore tasks with priority < N (default setting is 0)\n"
  71. "    -q    Remove CpuBlit from the system\n"
  72. "\n"
  73. "Keywords: BLITMODE=[ALWAYS|ONE|TWO|SHARE] BROKEN SINGLE MINTASKPRI=n QUIT\n"
  74. "\n"
  75. "See the documentation for details about starting CpuBlit from Workbench.\n";
  76.  
  77. /****************************************************************************
  78.  *
  79.  *              Globals
  80.  *
  81.  ***************************************************************************/
  82.  
  83. struct GfxBase           *GfxBase;
  84. struct IntuitionBase     *IntuitionBase;
  85. struct Library           *IconBase;
  86.  
  87. /*
  88.  *              Valid modes of operation for BlitMode
  89.  */
  90. #define BLIT_ALWAYS             0       /* Always use CPU for blits                                             */
  91. #define BLIT_ONE                1       /* Use CPU unless another task is ready to run  */
  92. #define BLIT_TWO                2       /* Use CPU unless more than one task is ready   */
  93. #define BLIT_SHARE              3       /* Use CPU unless CPU is already doing a blit   */
  94.  
  95. /*
  96.  *              This array corresponds to the above modes
  97.  */
  98. void (*BlitFuncs[])() = { StartBlit, Friend1, Friend2, ShareBlit };
  99.  
  100. /*
  101.  *              All the settings that can be set by the program
  102.  */
  103. struct Settings {
  104.         long    BlitMode;               /* Current mode of operation for blits  */
  105.         long    OnlySingle;             /* True if restricting blits to 1 bmap  */
  106.         long    Broken;                 /* True if handling broken software             */
  107.         BYTE    MinTaskPri;             /* All tasks less than this are ignored */
  108. } Settings = {
  109.         BLIT_ALWAYS, NO, NO, 0
  110. };
  111.  
  112. /*
  113.  *              Commands that we can send in a message
  114.  */
  115. #define MSG_GETVARS             0       /* Get copy of current CpuBlit settings */
  116. #define MSG_SETVARS             1       /* Update CpuBlit settings                              */
  117. #define MSG_QUIT                2       /* Remove background copy of CpuBlit    */
  118.  
  119. struct MyMsg {
  120.         struct  Message msg;    /* Standard message structure                   */
  121.         struct  Settings *vars; /* Settings used by CpuBlit                             */
  122.         int             command;                /* Requested operation                          */
  123.         int             result;                 /* True if command completed okay               */
  124. } MyMsg, *msg;
  125.  
  126. /*
  127.  *              Return codes passed back in result
  128.  */
  129. #define MSG_OKAY        0               /* Message was handled correctly                */
  130. #define MSG_REMOVED     1               /* CpuBlit was removed safely                   */
  131. #define MSG_FAILED      2               /* CpuBlit couldn't be removed                  */
  132.  
  133. /*
  134.  *              Scalar variables
  135.  */
  136. struct MsgPort *LocalPort;      /* Local port for returned messages             */
  137. long QuitFlag;                          /* True if user asks CpuBlit to quit    */
  138. long FromWorkbench;                     /* True if started from Workbench               */
  139. long AlreadyRunning;            /* True if CpuBlit already installed    */
  140.  
  141. /****************************************************************************
  142.  *
  143.  *              myexit(err)
  144.  *
  145.  *              Performs a small amount of cleanup and then exits to AmigaDos.
  146.  *              Principally, handles cleaning up if we were run from Workbench.
  147.  *
  148.  ***************************************************************************/
  149.  
  150. void myexit(err)
  151. {
  152.         if (LocalPort)
  153.                 DeletePort(LocalPort);
  154.  
  155.         exit(err);
  156. }
  157.  
  158. /****************************************************************************
  159.  *
  160.  *              SetVars(settings)
  161.  *
  162.  *              Sets the various CpuBlit flags according to the values in the
  163.  *              supplied Settings structure.
  164.  *
  165.  ***************************************************************************/
  166.  
  167. void SetVars(struct Settings *settings)
  168. {
  169.         BlitFunc        = BlitFuncs[settings->BlitMode];
  170.         OnlySingle      = settings->OnlySingle;
  171.         Broken          = settings->Broken;
  172.         MinTaskPri      = settings->MinTaskPri;
  173. }
  174.  
  175. /****************************************************************************
  176.  *
  177.  *              ParseOption()
  178.  *
  179.  *              Parses an option string, setting the appropriate field in the
  180.  *              master Settings structure. This routine handles both Unix-style
  181.  *              -opts and also ReadArgs/ToolTypes keywords. Returns true if
  182.  *              the option string made sense, false otherwise.
  183.  *
  184.  ***************************************************************************/
  185.  
  186. int ParseOption(char *opt)
  187. {
  188. #define MATCHSTR(s1,s2)         (!strnicmp(s1, s2, sizeof(s2)-1))
  189.  
  190.         if MATCHSTR(opt, "-a") {
  191.                 Settings.BlitMode       = BLIT_ALWAYS;
  192.                 Settings.OnlySingle     = NO;
  193.                 Settings.Broken         = NO;
  194.         }
  195.         else if MATCHSTR(opt, "-1")             Settings.BlitMode       = BLIT_ONE;
  196.         else if MATCHSTR(opt, "-2")             Settings.BlitMode       = BLIT_TWO;
  197.         else if MATCHSTR(opt, "-s")             Settings.BlitMode       = BLIT_SHARE;
  198.         else if MATCHSTR(opt, "-b")             Settings.Broken         = YES;
  199.         else if MATCHSTR(opt, "-o")             Settings.OnlySingle     = YES;
  200.         else if MATCHSTR(opt, "-q")             QuitFlag            = YES;
  201.         else if (MATCHSTR(opt, "-p") && opt[2])
  202.                 Settings.MinTaskPri = atoi(opt+2);
  203.         else if MATCHSTR(opt, "BLITMODE") {
  204.                 char *p = opt + 8;
  205.                 if (*p++ && *p) {
  206.                         if              MATCHSTR(p, "ALWAYS")   Settings.BlitMode       = BLIT_ALWAYS;
  207.                         else if MATCHSTR(p, "ONE")              Settings.BlitMode       = BLIT_ONE;
  208.                         else if MATCHSTR(p, "TWO")              Settings.BlitMode       = BLIT_TWO;
  209.                         else if MATCHSTR(p, "SHARE")    Settings.BlitMode       = BLIT_SHARE;
  210.                 } else return (0);
  211.         }
  212.         else if MATCHSTR(opt, "BROKEN") {
  213.                 char *p = opt + 6;
  214.                 if (*p++ && MATCHSTR(p, "NO"))          Settings.Broken         = NO;
  215.                 else                                                            Settings.Broken         = YES;
  216.         }
  217.         else if MATCHSTR(opt, "SINGLE") {
  218.                 char *p = opt + 6;
  219.                 if (*p++ && MATCHSTR(p, "NO"))          Settings.OnlySingle     = NO;
  220.                 else                                                            Settings.OnlySingle = YES;
  221.         }
  222.         else if MATCHSTR(opt, "MINTASKPRI") {
  223.                 char *p = opt + 10;
  224.                 if (*p++ && *p)                                         Settings.MinTaskPri = atoi(p);
  225.                 else return (0);
  226.         }
  227.         else if MATCHSTR(opt, "QUIT")                   QuitFlag = YES;
  228.         else  return (0);
  229.  
  230.         return (1);
  231. }
  232.  
  233. /****************************************************************************
  234.  *
  235.  *              MyFindPort(name)
  236.  *
  237.  *              Replacement for the FindPort() in exec.library. Under 1.3, FindPort()
  238.  *              will cause Enforcer hits since FFS partitions create public ports
  239.  *              that have no name (this is a no-no), and FindPort() doesn't check
  240.  *              for null names.
  241.  *
  242.  *              Even though this isn't really CpuBlit's problem, I had quite a few
  243.  *              reports of CpuBlit causing Enforcer hits which turned out to be
  244.  *              because of this, so I've added this workaround to keep people
  245.  *              happy.
  246.  *
  247.  *              Commodore made FindPort() (actually FindName()) a bit more robust
  248.  *              under Kickstart 2.0, so in that case we can safely use the standard
  249.  *              routine.
  250.  *
  251.  ***************************************************************************/
  252.  
  253. struct MsgPort *MyFindPort(char *name)
  254. {
  255.         struct Node *nd;
  256.         extern struct ExecBase *SysBase;
  257.  
  258.         if (SysBase->LibNode.lib_Version >= 36)
  259.                 return (FindPort(name));
  260.  
  261.         Forbid();
  262.         for (nd = SysBase->PortList.lh_Head; nd; nd = nd->ln_Succ)
  263.                 if (nd->ln_Name && !strcmp(nd->ln_Name, name))
  264.                         break;
  265.         Permit();
  266.         return (struct MsgPort *)nd;
  267. }
  268.  
  269.  
  270. /****************************************************************************
  271.  *
  272.  *              mainloop()
  273.  *
  274.  *              This is the main event loop. It sits waiting for a message from
  275.  *              other invocations of CpuBlit, which tell it to either change the
  276.  *              current settings or to remove itself.
  277.  *
  278.  ***************************************************************************/
  279.  
  280. void mainloop(void)
  281. {
  282.         struct MsgPort *MyPort;
  283.         int installed = 1;
  284.         __fptr *BltBitMapPtr = (__fptr *)BltBitMapAddress;
  285.  
  286.         /*
  287.          *              We have to create our rendezvous port here rather than in the
  288.          *              mainline, since the message port depends on task information etc.
  289.          *              This is not altogether satisfactory since if it fails, there is
  290.          *              no way to tell the user (as a background task, we have no stdin
  291.          *              or stdout). But since port creation is unlikely to fail anyway,
  292.          *              it's not a big problem.
  293.          */
  294.         MyPort = CreatePort(PORTNAME, 0);
  295.         if (!MyPort)
  296.                 return;
  297.  
  298.         /*
  299.          *              Now have to open graphics.library, so that we can add in our
  300.          *              new patch. As above, if this fails there is no easy way to
  301.          *              tell the user. However, at least it won't crash the system.
  302.          */
  303.         GfxBase = (struct GfxBase *) OpenLibrary("graphics.library", 33);
  304.         if (!GfxBase)
  305.                 return;
  306.  
  307.         *BltBitMapPtr = SetFunction((struct Library *) GfxBase, BltBitMap_LVO, NewBltBitMap);
  308.  
  309.         /*
  310.          *              Now wait a message from another copy of CpuBlit. This will
  311.          *              either contain an updated command line argument or else a
  312.          *              request to quit.
  313.          */
  314.         do {
  315.                 __fptr oldptr;
  316.  
  317.                 WaitPort(MyPort);
  318.                 while ((msg = (struct MyMsg *)GetMsg(MyPort)) != NULL) {
  319.                         switch (msg->command) {
  320.  
  321.                         case MSG_GETVARS:
  322.                                 memcpy(msg->vars, &Settings, sizeof(Settings));
  323.                                 break;
  324.  
  325.                         case MSG_SETVARS:
  326.                                 memcpy(&Settings, msg->vars, sizeof(Settings));
  327.                                 SetVars(&Settings);
  328.                                 break;
  329.  
  330.                         case MSG_QUIT:
  331.                                 /*
  332.                                  *              Try and remove ourselves. We have to surround this
  333.                                  *              with Forbid() to make sure that no other tasks manage
  334.                                  *              to call BltBitMap() in the case where we restore the
  335.                                  *              original vector and then realise that its current
  336.                                  *              replacement actually pointed to something other than
  337.                                  *              CpuBlit.
  338.                                  */
  339.                                 Forbid();
  340.                                 oldptr = SetFunction(GfxBase, BltBitMap_LVO, *BltBitMapPtr);
  341.                                 if (oldptr == NewBltBitMap) {
  342.                                         installed = 0;
  343.                                         msg->result = MSG_REMOVED;
  344.                                 } else {
  345.                                         SetFunction(GfxBase, BltBitMap_LVO, oldptr);
  346.                                         msg->result = MSG_FAILED;
  347.                                 }
  348.                                 Permit();
  349.                         }
  350.                         ReplyMsg(msg);
  351.                 }
  352.         } while (installed);
  353.  
  354.         /*
  355.          *              Now our patch has been removed, it only remains to free up the
  356.          *              code. It is possible that someone is still in our blitter code.
  357.          *              We can determine this fairly safely by looking at UsageCount;
  358.          *              if this is -1, nobody is in our code. Otherwise, we wait until
  359.          *              it is -1 (delaying for a little while inbetween to give programs
  360.          *              a chance to run).
  361.          *
  362.          *              We also set the blitter test function to ExitBlit, so that if
  363.          *              someone does slip through our test and end up inside our code,
  364.          *              they will get rerouted back to the normal blitter code almost
  365.          *              immediately.
  366.          */
  367.         DeletePort(MyPort);
  368.         BlitFunc = ExitBlit;
  369.  
  370.         while (UsageCount != -1)
  371.                 Delay(10);                              /* Wait 0.2 seconds */
  372.  
  373.         /*
  374.          *              Now we're completely finished so we can close the libraries
  375.          *              and exit.
  376.          */
  377.         CloseLibrary((struct Library *) GfxBase);
  378. }
  379.  
  380.  
  381. /****************************************************************************
  382.  *
  383.  *              Mainline
  384.  *
  385.  ***************************************************************************/
  386.  
  387. void main(int argc, char **argv)
  388. {
  389.         struct MsgPort *BlitPort;
  390.         int i;
  391.  
  392.         FromWorkbench = (argc == 0);
  393.  
  394.         /*
  395.          *              Now see if CpuBlit is already running in
  396.          *              the background. If it is, then get a copy of the settings it
  397.          *              is currently using.
  398.          */
  399.         BlitPort = MyFindPort(PORTNAME);
  400.         if (BlitPort) {
  401.                 /*
  402.                  *              The new blit routine has already been installed. So, send
  403.                  *              it a message giving the command line options (if any)
  404.                  *              to the remote routine, telling it to update its own options.
  405.                  */
  406.                 AlreadyRunning = YES;
  407.                 LocalPort = CreatePort(NULL, 0);
  408.                 if (!LocalPort) {
  409.                         if (!FromWorkbench)
  410.                                 print("CpuBlit: couldn't create local message port.\n");
  411.                         myexit(10);
  412.                 }
  413.                 MyMsg.msg.mn_ReplyPort  = LocalPort;
  414.                 MyMsg.command                   = MSG_GETVARS;
  415.                 MyMsg.vars                      = &Settings;
  416.                 PutMsg(BlitPort, &MyMsg);
  417.                 WaitPort(LocalPort);
  418.                 GetMsg(LocalPort);
  419.         }
  420.         /*
  421.          *              Now parse the command line options, modifying our local copy
  422.          *              of Settings accordingly.
  423.          */
  424.         if (FromWorkbench) {
  425.                 extern struct WBStartup *WBenchMsg;
  426.                 struct WBArg *wbarg = WBenchMsg->sm_ArgList;
  427.  
  428.                 IconBase = (struct Library *)OpenLibrary("icon.library", 33);
  429.                 if (!IconBase)
  430.                         myexit(5);
  431.  
  432.                 /*
  433.                  *              Now walk down all the icons we've been given (probably
  434.                  *              just our own tool icon) and parse the arguments present
  435.                  *              in each one.
  436.                  */
  437.                 for (i = 0; i < WBenchMsg->sm_NumArgs; i++, wbarg++) {
  438.                         struct DiskObject *dobj;
  439.                         char **tooltypes;
  440.                         BPTR olddir;
  441.  
  442.                         if (wbarg->wa_Lock && *wbarg->wa_Name) {
  443.                                 olddir = CurrentDir(wbarg->wa_Lock);
  444.                                 if (dobj = GetDiskObject(wbarg->wa_Name)) {
  445.                                         for (tooltypes = dobj->do_ToolTypes;
  446.                                                                                         *tooltypes; tooltypes++)
  447.                                                 ParseOption(*tooltypes);
  448.                                 }
  449.                                 FreeDiskObject(dobj);
  450.                                 CurrentDir(olddir);
  451.                         }
  452.                 }
  453.                 CloseLibrary((struct Library *) IconBase);
  454.         } else {
  455.                 /*
  456.                  *              Plain jane CLI startup
  457.                  */
  458. #if DEBUG
  459.                 if (argv[1][0] == '!') {
  460.                         static buf[1000];
  461.                         sprintf(buf,
  462.                                 "Blitmode   = %d\n"
  463.                                 "OnlySingle = %d\n"
  464.                                 "Broken     = %d\n"
  465.                                 "MinTaskPri = %d\n",
  466.                                 Settings.BlitMode, Settings.OnlySingle,
  467.                                 Settings.Broken, Settings.MinTaskPri);
  468.                         print(buf);
  469.                         exit(5);
  470.                 }
  471. #endif
  472.                 for (i = 1; i < argc; i++) {
  473.                         if (!ParseOption(argv[i])) {
  474.                                 print(HelpMsg);
  475.                                 myexit(5);
  476.                         }
  477.                 }
  478.         }
  479.  
  480.         /*
  481.          *              Now we either send the options to the remote copy of CpuBlit
  482.          *              or install ourselves in the background.
  483.          */
  484.         if (AlreadyRunning) {
  485.                 if (QuitFlag)
  486.                         MyMsg.command = MSG_QUIT;
  487.                 else
  488.                         MyMsg.command = MSG_SETVARS;
  489.                 PutMsg(BlitPort, &MyMsg);
  490.                 WaitPort(LocalPort);
  491.                 GetMsg(LocalPort);
  492.                 if (FromWorkbench && MyMsg.result == MSG_FAILED) {
  493.                         IntuitionBase = (struct IntuitionBase *)
  494.                                                         OpenLibrary("intuition.library", 33);
  495.                         if (IntuitionBase) {
  496.                                 DisplayBeep(0); /* Flash all screens -- pretty rude */
  497.                                 CloseLibrary((struct Library *) IntuitionBase);
  498.                         }
  499.                 }
  500.                 if (!FromWorkbench) {
  501.                         if (MyMsg.result == MSG_REMOVED)
  502.                                 print("CpuBlit removed successfully.\n");
  503.                         else if (MyMsg.result == MSG_FAILED) {
  504.                                 print(
  505. "Couldn't remove CpuBlit; someone else has patched BltBitMap. Please remove\n"
  506. "any other utilities you have installed and then try again.\n");
  507.                                 myexit(5);
  508.                         }
  509.                 }
  510.                 myexit(0);
  511.         }
  512.  
  513.         /*
  514.          *              This is the first time we are being run. If we were run from
  515.          *              the CLI, detach ourselves (and allow the current process to
  516.          *              return to the CLI immediately). If we were run from Workbench,
  517.          *              then just call the message handling code directly and wait
  518.          *              until another copy of CpuBlit asks us to return.
  519.          */
  520.         if (QuitFlag) {
  521.                 if (!FromWorkbench)
  522.                         print("CpuBlit hasn't been installed yet.\n");
  523.                 myexit(5);
  524.         }
  525.  
  526.         SetVars(&Settings);
  527.         if (FromWorkbench)
  528.                 mainloop();
  529.         else {
  530.                 if (!res(NAME, 5, mainloop, 4000)) {
  531.                         print("Couldn't spawn background CpuBlit task.\n");
  532.                         myexit(10);
  533.                 }
  534.                 print(SIGNON);
  535.         }
  536.         myexit(0);
  537. }
  538.